package com.sxhuayuan.parking.interceptor;

import java.math.BigDecimal;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.alibaba.fastjson.JSON;
import com.alipay.api.request.AlipaySystemOauthTokenRequest;
import com.alipay.api.request.AlipayUserInfoShareRequest;
import com.alipay.api.response.AlipaySystemOauthTokenResponse;
import com.alipay.api.response.AlipayUserInfoShareResponse;
import com.sxhuayuan.parking.compenent.Message;
import com.sxhuayuan.parking.compenent.alipay.IAlipayMultiAppClient;
import com.sxhuayuan.parking.compenent.domain.Setting;
import com.sxhuayuan.parking.compenent.shiro.AccountType;
import com.sxhuayuan.parking.compenent.shiro.AuthenticationToken;
import com.sxhuayuan.parking.compenent.shiro.realm.MemberAuthenticationRealm;
import com.sxhuayuan.parking.compenent.web.RespMessage;
import com.sxhuayuan.parking.config.Platform;
import com.sxhuayuan.parking.entity.Member;
import com.sxhuayuan.parking.entity.Member.Gender;
import com.sxhuayuan.parking.entity.OemConfig;
import com.sxhuayuan.parking.entity.ParkChannel;
import com.sxhuayuan.parking.entity.ParkingLot;
import com.sxhuayuan.parking.exception.ViewException;
import com.sxhuayuan.parking.service.MemberService;
import com.sxhuayuan.parking.service.OemConfigService;
import com.sxhuayuan.parking.service.ParkChannelService;
import com.sxhuayuan.parking.service.ParkingLotService;
import com.sxhuayuan.parking.utils.AlipayUtils;
import com.sxhuayuan.parking.utils.DeviceUtil;
import com.sxhuayuan.parking.utils.SettingUtils;
import com.sxhuayuan.parking.utils.WebUtils;
import com.sxhuayuan.parking.utils.WeixinUtils;

import weixin.popular.api.SnsAPI;
import weixin.popular.bean.sns.SnsToken;
import weixin.popular.bean.user.User;

@Component
public class MemberOpenidInterceptor extends HandlerInterceptorAdapter {

	Logger log = LoggerFactory.getLogger(getClass());

	@Autowired
	MemberService memberService;
	@Autowired
	ParkChannelService exitService;
	@Autowired
	ParkingLotService lotService;
	@Autowired
	IAlipayMultiAppClient alipayClient;
	@Resource(name = "memberAuthenticationRealm")
	MemberAuthenticationRealm memberAuthenticationRealm;
	@Autowired
	OemConfigService oemConfigService;

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		String userAgent = request.getHeader("User-Agent");
		boolean isFromWeixin = DeviceUtil.isFromWeixin(userAgent);
		boolean isFromAlipay = DeviceUtil.isFromAlipay(userAgent);
		Subject subject = SecurityUtils.getSubject();
		Session session = subject.getSession();
		// log.debug("uri={}, subject: {}, session={}", request.getRequestURI(),
		// subject, session.getId());
		// if (SettingUtils.get().getIsDebug()) {
		// if (!subject.isAuthenticated()) {
		// saveContextInSession(request, session);
		// Member member = memberService.findOne(1L);
		// log.debug("member debug 登录, {}", member);
		// AuthenticationToken usernamePasswordToken = new
		// AuthenticationToken(AccountType.member, member.getId() + "@@" +
		// member.getOpenid(), member.getOpenid(), null, null, false,
		// null);
		// log.debug("member登录, {}", member);
		// subject = SecurityUtils.getSecurityManager().login(subject,
		// usernamePasswordToken);
		// // 登录后如果直接放行会取不到pricipal对象，可能shiro还没有执行相关代码，所以重定向一次
		// String requestUrlWithoutCode = getRequestUrlWithoutCode(request);
		// log.debug("再跳转1次: {}", requestUrlWithoutCode);
		// response.sendRedirect(requestUrlWithoutCode);
		// return false;
		// }
		// return true;
		// }
		if (!isFromWeixin && !isFromAlipay) {
			if (WebUtils.isAjaxRequest(request)) {
				response.getWriter().write(JSON.toJSONString(RespMessage.error(Message.MEMBER_SESSION_TIMEOUT)));
			} else {
				throw new ViewException(Message.MEMBER_SESSION_TIMEOUT);
			}
			return false;
		}
		if (memberService.isAuthenticated()) {
			updateContextInSession(request, session);
			return true;
		} else {
			String requestType = request.getHeader("X-Requested-With");
			if (requestType != null && requestType.equalsIgnoreCase("XMLHttpRequest")) {
				response.addHeader("loginStatus", "accessDenied");
				response.sendError(HttpServletResponse.SC_FORBIDDEN);
				return false;
			} else {
				if (request.getMethod().equalsIgnoreCase("GET")) {
					Setting setting = SettingUtils.get();
					String code = request.getParameter("code");
					if (isFromAlipay)
						code = request.getParameter("auth_code");
					OemConfig oem = null;
					if (StringUtils.isEmpty(code)) {

						saveContextInSession(request, session);

						// 查找oem配置
						String parkingLotSn = session.getAttribute(ParkingLot.SESSION_ATTR_NAME).toString();
						ParkingLot lot = lotService.findBySn(parkingLotSn);
						if (lot != null && lot.getAgent() != null) {
							// 缓存oem配置到session
							oem = oemConfigService.findByParkingLot(lot.getId());
							if (SettingUtils.get().getIsDebug()) {
								oem = oemConfigService.findOne(3L);
							}
							session.setAttribute(OemConfig.SESSION_ATTR_NAME, oem);
						}

						String requestUrl = request.getQueryString() != null ? request.getRequestURL().append("?").append(request.getQueryString()).toString()
								: request.getRequestURL().toString();

						String redirectUrl = null;

						if (isFromWeixin) {
							String appid = oem.getWxAppid();
							redirectUrl = WeixinUtils.connectOauth2Authorize(appid, requestUrl, setting.getIsGetUserInfo(), null);
							// log.debug("redirectUrl={}", redirectUrl);
							response.sendRedirect(redirectUrl);
						} else if (isFromAlipay) {
							String appid = oem.getAlipayAppid();
							redirectUrl = AlipayUtils.connectOauth2Authorize(appid, requestUrl, setting.getIsGetUserInfo(), null);
							// log.debug("redirectUrl={}", redirectUrl);
							response.sendRedirect(redirectUrl);
						} else {
							throw new ViewException(Message.COMMON_CLIENT_NOT_SUPPORT);
						}
					} else {
						Member member = null;
						oem = (OemConfig) session.getAttribute(OemConfig.SESSION_ATTR_NAME);
						// 处理回调code
						if (isFromWeixin) {
							SnsToken accessToken = SnsAPI.oauth2AccessToken(oem.getWxAppid(), oem.getWxSecret(), code);
							member = memberService.findByOpenId(accessToken.getOpenid());
							if (member == null) {
								member = new Member();
							}
							if (setting.getIsGetUserInfo()) {
								// log.debug("获取会员信息");
								User userinfo = SnsAPI.userinfo(accessToken.getAccess_token(), accessToken.getOpenid(), "zh_CN", 1);
								member.setNickname(userinfo.getNickname_emoji());
								member.setHeadimg(userinfo.getHeadimgurl());
								member.setAmount(BigDecimal.ZERO);
								member.setGender(Gender.fromWx(userinfo.getSex()));
							}
							member.setOpenid(accessToken.getOpenid());
							member.setPlatform(Platform.weixin);
							memberService.save(member);
						}
						if (isFromAlipay) {
							if (!oem.isAlipayConfigOK()) {
								throw new ViewException("Alipay oem config error! Please check it!");
							}
							AlipaySystemOauthTokenRequest tokenRequest = new AlipaySystemOauthTokenRequest();
							tokenRequest.setCode(code);
							// 值为authorization_code时，代表用code换取；值为refresh_token时，代表用refresh_token换取
							tokenRequest.setGrantType("authorization_code");
							AlipaySystemOauthTokenResponse alipaySystemOauthTokenResponse = alipayClient.execute(oem, tokenRequest);
							if (!alipaySystemOauthTokenResponse.isSuccess()) {
								throw new ViewException(Message.LOGIN_ACCOUNT_AUTHENTICATION);
							}
							member = memberService.findByOpenId(alipaySystemOauthTokenResponse.getUserId());
							if (member == null) {
								member = new Member();
							}

							if (setting.getIsGetUserInfo()) {
								String accessToken = alipaySystemOauthTokenResponse.getAccessToken();
								AlipayUserInfoShareRequest alipayUserInfoShareRequest = new AlipayUserInfoShareRequest();
								AlipayUserInfoShareResponse alipayUserInfoShareResponse = alipayClient.execute(oem, alipayUserInfoShareRequest, accessToken);
								if (!alipayUserInfoShareResponse.isSuccess()) {
									throw new ViewException(Message.LOGIN_ACCOUNT_AUTHENTICATION);
								}
								member.setNickname(alipayUserInfoShareResponse.getNickName());
								member.setHeadimg(alipayUserInfoShareResponse.getAvatar());
								member.setAmount(BigDecimal.ZERO);
								member.setGender(Gender.fromAli(alipayUserInfoShareResponse.getGender()));
							}
							member.setOpenid(alipaySystemOauthTokenResponse.getUserId());
							member.setPlatform(Platform.alipay);
							memberService.save(member);
						}
						if (member != null) {
							AuthenticationToken usernamePasswordToken = new AuthenticationToken(AccountType.member, member.getId() + "@@" + member.getOpenid(), member.getOpenid(), null,
									null, false, null);
							log.debug("member登录, {}, 客户端:{}", member, userAgent);
							subject = SecurityUtils.getSecurityManager().login(subject, usernamePasswordToken);
							// 登录后如果直接放行会取不到pricipal对象，可能shiro还没有执行相关代码，所以重定向一次
							String requestUrlWithoutCode = getRequestUrlWithoutCode(request);
							// log.debug("再跳转1次: {}", requestUrlWithoutCode);
							// 必须再跳转1次，否则会拿不到currentMember
							response.sendRedirect(requestUrlWithoutCode);
							return false;
						} else {
							throw new ViewException(Message.LOGIN_ACCOUNT_AUTHENTICATION);
						}
					}

				} else {
					throw new ViewException(Message.COMMON_CLIENT_NOT_SUPPORT);
				}
				return false;
			}
		}
	}

	private boolean updateContextInSession(HttpServletRequest request, Session session) {
		String exitSn = null, lotSn = null;
		String uri = request.getRequestURI();
		String type = null;
		Pattern pattern = Pattern.compile("^/member/park/(qr|pre|pay)/([a-zA-Z0-9]{1,})$");
		Matcher matcher = pattern.matcher(uri);
		if (matcher.find()) {
			type = matcher.group(1);
			switch (type) {
			case "qr":// 出口扫码
				exitSn = matcher.group(2);
				break;
			case "pre":// 预支付
				lotSn = matcher.group(2);
				break;
			case "pay":// 出口支付
				exitSn = matcher.group(2);
				break;
			default:
				break;
			}
			if (!StringUtils.isEmpty(exitSn)) {
				ParkChannel exit = exitService.findBySn(exitSn);
				if (exit != null) {
					lotSn = exit.getParkingLot().getSn();
					if ("qr".equals(type)) {
						// 出口扫码判断依据改为出口编号是否已获取，所以这里把出口码清空掉
						exitSn = null;
					}
				}
			}
			// log.debug("缓存出口exitSn={}, lotSn={}", exitSn, lotSn);
			session.setAttribute(ParkChannel.SESSION_ATTR_NAME, exitSn);
			session.setAttribute(ParkingLot.SESSION_ATTR_NAME, lotSn);
			return true;
		}
		return false;
	}

	/**
	 * 缓存车场，出口等上下文信息
	 * 
	 * @param request
	 * @param session
	 */
	private void saveContextInSession(HttpServletRequest request, Session session) {
		if (!updateContextInSession(request, session))
			throw new ViewException(RespMessage.Status.ERROR, "请扫码打开");
	}

	private String getRequestUrlWithoutCode(HttpServletRequest request) {
		final StringBuffer url = request.getRequestURL().append("?");
		request.getParameterMap().forEach((k, v) -> {
			if ("code".equalsIgnoreCase(k))
				return;
			url.append(k).append("=").append(v[0]).append("&");
		});
		if (url.toString().endsWith("?") || url.toString().endsWith("&"))
			url.deleteCharAt(url.length() - 1);
		return url.toString();
	}
}
